package com.xaslsoft.xaslsoft.device.net;

import cn.hutool.core.io.BufferUtil;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.HexUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.socket.nio.ChannelHandler;
import cn.hutool.socket.nio.NioServer;
import com.magicrf.uhfreaderlib.reader.Tools;
import com.xaslsoft.xaslsoft.device.rfid.usb.product.EPC;
import com.xaslsoft.xaslsoft.device.utils.ByteUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.*;

import static com.xaslsoft.xaslsoft.device.net.IDataHandler.SOCKET_CHANNELS;

/**
 * TcpClient
 */
@Slf4j
public class TcpService implements ChannelHandler {
	private String hexStr = "BB00220000227E";
	private boolean send = true;
	private String clientIp;
	String PING = "383838";
	public static Set<String> TAG_SET = new HashSet<>();
	private static ArrayList<EPC> listEPC = new ArrayList<>();
	static TcpService service;

	public TcpService(int port, IDataHandler dataHandler) {
		ThreadUtil.execAsync(() -> {
			NioServer server = new NioServer(port);
			server.setChannelHandler(TcpService.this::handle);
			setDataHandler(dataHandler);
			server.listen();
		});
	}

	public void sendData(String clientIp, String hexStr) {
		this.hexStr = hexStr;
		this.clientIp = clientIp;
		this.send = true;
	}

	public static void main(String[] args) throws IOException {
		service = new TcpService(9088, (socketChannel, hex) -> {
			String[] tags = StrUtil.subBetweenAll(hex, "BB0222", "7E");
			for (String tag : tags) {
				StringBuilder sb = new StringBuilder("BB0222");
				String epc = sb.append(tag).append("7E").toString();
				TAG_SET.add(epc);
				byte[] bytes = HexUtil.decodeHex(epc);
				service.process(bytes);
			}
		});
		do {
			ThreadUtil.sleep(2000);
			TAG_SET.clear();
			listEPC.clear();
			service.sendData("192.168.1.5", "BB00220000227E");
		} while (true);


	}

	public static void doWrite(SocketChannel channel, String hex) throws IOException {
		log.debug("[{}]:------发送数据--------> {}", channel.getRemoteAddress(), hex);
		//将缓冲数据写入渠道，返回给客户端
		channel.write(BufferUtil.create(ByteUtils.hexStr2Byte(hex)));
	}

	public void setDataHandler(IDataHandler dataHandler) {
		this.dataHandler = dataHandler;
	}

	IDataHandler dataHandler;

	@Override
	@Async
	public void handle(SocketChannel sc) throws Exception {
		ByteBuffer readBuffer = ByteBuffer.allocate(96);
		try {
			String address = sc.getRemoteAddress().toString();
			String ip = StrUtil.subBetween(address, "/", ":");
			if (!SOCKET_CHANNELS.containsKey(ip)) {
				SOCKET_CHANNELS.put(ip, sc);
			}
			//从channel读数据到缓冲区
			int readBytes = sc.read(readBuffer);
			if (readBytes > 0) {
				readBuffer.flip();
				// 要读取的字节长度
				byte[] bytes = new byte[readBuffer.remaining()];
				//将缓冲区的数据读到bytes数组
				readBuffer.get(bytes);
				String body = ByteUtils.byteArrayToHexString(bytes);
				if (StrUtil.equals(body, PING)) {
					if (send) {
						SocketChannel socketChannel = SOCKET_CHANNELS.get(clientIp);
						if (Objects.nonNull(socketChannel)) {
							try {
								if (StrUtil.equals(body, PING)) {
									//Console.log("[{}]:------心跳数据--------> {}", sc, body);
									if (StrUtil.isNotEmpty(hexStr) && send) {
										doWrite(socketChannel, hexStr);
										hexStr = "";
										send = false;
									}
								}
							} catch (IOException exception) {
								exception.printStackTrace();
							}
						}
					}
				} else {
					log.debug("[{}]:-----接收数据--------> {}", sc.getRemoteAddress(), body);
					dataHandler.onReceive(sc, body);
				}
			} else if (readBytes < 0) {
				IoUtil.close(sc);
			}
		} catch (IOException e) {
			throw new IORuntimeException(e);
		}
	}

	public List<EPC> addToList(final List<EPC> list, final String epc) {
		if (StrUtil.isNotEmpty(epc)) {
			EPC epcTag = new EPC();
			epcTag.setEpc(epc);
			epcTag.setCount(1);
			list.add(epcTag);
		}
		return list;
	}


	@Async
	public void process(byte[] response) {
		if (response != null) {
			int responseLength = response.length;
			int start = 0;
			if (responseLength >= 12) {
				while (responseLength > 11) {
					int paraLen = response[start + 4] & 255;
					int singleCardLen = paraLen + 7;
					if (singleCardLen > responseLength) {
						break;
					}
					byte[] sigleCard = new byte[singleCardLen];
					System.arraycopy(response, start, sigleCard, 0, singleCardLen);
					byte[] resolve = this.handlerResponse(sigleCard);
					if (resolve != null) {
						if (paraLen > 5) {
							byte[] epcBytes = new byte[paraLen - 5];
							System.arraycopy(resolve, 4, epcBytes, 0, paraLen - 5);
							String epc = Tools.Bytes2HexString(epcBytes, epcBytes.length);
							addToList(listEPC, epc);
							log.info("-------------EPC---------------{}", epc);
						}
					}
					start += singleCardLen;
					responseLength -= singleCardLen;
				}
			}
		}
	}

	private byte[] handlerResponse(byte[] response) {
		byte[] data = null;
		int responseLength = response.length;
		if (response[0] != -69) {
			log.error("handlerResponse-----{}", "head error");
			return data;
		} else if (response[responseLength - 1] != 126) {
			log.error("handlerResponse-----{}", "end error");
			return data;
		} else if (responseLength < 7) {
			return data;
		} else {
			int lengthHigh = response[3] & 255;
			int lengthLow = response[4] & 255;
			int dataLength = lengthHigh * 256 + lengthLow;
			byte crc = this.checkSum(response);
			if (crc != response[responseLength - 2]) {
				log.error("handlerResponse-----{}", "crc error");
				return data;
			} else {
				if (dataLength != 0 && responseLength == dataLength + 7) {
					data = new byte[dataLength + 1];
					data[0] = response[2];
					System.arraycopy(response, 5, data, 1, dataLength);

				}

				return data;
			}
		}
	}

	public byte checkSum(byte[] data) {
		byte crc = 0;
		for (int i = 1; i < data.length - 2; ++i) {
			crc += data[i];
		}
		return crc;
	}


}
